///================================================================///
/// igmp.click
///
/// This script implements a small IP network inside click. Several
/// compound elements are used to create the different network entities.
///
/// This script is part of the assignment of the 
/// course 'Telecommunicatiesystemen 2006-2007'. The assignment explains
/// in more detail the network architecture.
///
/// Authors: Bart Braem & Michael Voorhaen
///================================================================///

///===========================================================================///
/// An IP router with 1 interface.
elementclass IPRouter1int 
{
$addr_info, $gateway
|

///
    imgpclient :: IGMPClient(SRC $addr_info)
/// ===== Begin - IP Routing Path - Network Layer
	// Shared IP input path and routing table
	ip :: Strip(14)
	-> CheckIPHeader()
	-> imgpclient; //TODO: this is quite messy, we should classify on mcast message bellow in staticIpLookop


imgpclient[1]
	-> rt :: StaticIPLookup(
		$addr_info:ip/32 0,
		$addr_info:ipnet 0,
		0.0.0.0/0.0.0.0 $gateway 1);
/// ===== End - IP Routing Path - Network Layer

imgpclient[0]
    -> IPEncap(2, $addr_info, 224.0.0.22, TTL 1) 
    -> EtherEncap(0x0800, $addr_info, $gateway)
    -> ToDump("ReportDump.dump");
    
/// ===== Begin - ARP Path - MAC to Network Layer
	// ARP responses are copied to each ARPQuerier and the host.
	arpt :: Tee(1);
	
	// Input and output paths for eth0
	c0 :: Classifier(12/0806 20/0001, 12/0806 20/0002, -);
	input[0] -> HostEtherFilter($addr_info:eth) -> c0;
	c0[0] -> ar0 :: ARPResponder($addr_info) -> [0]output;
	arpq0 :: ARPQuerier($addr_info, $addr_info) -> [0]output;
	c0[1] -> arpt;
	arpt[0] -> [1]arpq0;
	c0[2] -> Paint(1) -> ip;
/// ===== End - ARP Path - MAC to Network Layer

/// ===== Begin - Local Delivery - Transport and Application Layer	
/// ===== Note: any packets meant for this node should be processed here, or passed to the output
	// Local delivery
	rt[0] -> [1]output; 
/// ===== End - Local Delivery - Transport and Application Layer

/// ===== Begin - Forwarding Path - Network Layer	
	rt[1] -> DropBroadcasts
	-> gio0 :: IPGWOptions($addr_info)
	-> FixIPSrc($addr_info)
	-> dt0 :: DecIPTTL
	-> fr0 :: IPFragmenter(1500)
	-> [0]arpq0;
	dt0[1] -> ICMPError($addr_info, timeexceeded) -> rt;
	fr0[1] -> ICMPError($addr_info, unreachable, needfrag) -> rt;
	gio0[1] -> ICMPError($addr_info, parameterproblem) -> rt;
/// ===== End - Forwarding Path - Network Layer	
}

///===========================================================================///
/// An IP router with 3 interfaces.
elementclass IPRouter3int
{
$server_address, $client_address1, $client_address2
|

/// ===== Begin - IP Routing Path - Network Layer
	// Shared IP input path and routing table
	ip :: Strip(14)
	-> CheckIPHeader()
	-> rt :: StaticIPLookup(
		$server_address:ip/32 0,
		$client_address1:ip/32 0,
		$client_address2:ip/32 0,
		$server_address:ipnet 1,
		$client_address1:ipnet 2,
		$client_address2:ipnet 3);
/// ===== End - IP Routing Path - Network Layer
	
/// ===== Begin - ARP Path - MAC to Network Layer	
/// ===== Note: incoming Ethernet encapsulated packets, per interface
	// ARP responses are copied to each ARPQuerier and the host.
	arpt :: Tee (3);
	
	// Input and output paths for eth0
	c0 :: Classifier(12/0806 20/0001, 12/0806 20/0002, -);
	input[0] -> HostEtherFilter($server_address:eth) -> c0;
	c0[0] -> ar0 :: ARPResponder($server_address) -> [0]output;
	arpq0 :: ARPQuerier($server_address, $server_address) -> [0]output;
	c0[1] -> arpt;
	arpt[0] -> [1]arpq0;
	c0[2] -> Paint(1) -> ip;
	
	// Input and output paths for eth1
	c1 :: Classifier(12/0806 20/0001, 12/0806 20/0002, -);
	input[1] -> HostEtherFilter($client_address1:eth) -> c1;
	c1[0] -> ar1 :: ARPResponder($client_address1) -> [1]output;
	arpq1 :: ARPQuerier($client_address1, $client_address1) -> [1]output;
	c1[1] -> arpt;
	arpt[1] -> [1]arpq1;
	c1[2] -> Paint(2) -> ip;

	// Input and output paths for eth1
	c2 :: Classifier(12/0806 20/0001, 12/0806 20/0002, -);
	input[2] -> HostEtherFilter($client_address2:eth) -> c2;
	c2[0] -> ar2 :: ARPResponder($client_address2) -> [2]output;
	arpq2 :: ARPQuerier($client_address2, $client_address2) -> [2]output;
	c2[1] -> arpt;
	arpt[2] -> [1]arpq2;
	c2[2] -> Paint(3) -> ip;
/// ===== End - ARP Path - MAC to Network Layer

/// ===== Begin - Local Delivery - Transport and Application Layer	
/// ===== Note: any packets meant for this node should be processed here, or passed to the output	
	// Local delivery
	rt[0] -> [3]output; 
/// ===== End - Local Delivery - Transport and Application Layer

/// ===== Begin - Forwarding Path - Network Layer
/// ===== Note: outgoing IP packets, per interface	
	// Forwarding path for eth0
	rt[1] -> DropBroadcasts
	-> cp0 :: PaintTee(1)
	-> gio0 :: IPGWOptions($server_address)
	-> FixIPSrc($server_address)
	-> dt0 :: DecIPTTL
	-> fr0 :: IPFragmenter(1500)
	-> [0]arpq0;
	dt0[1] -> ICMPError($server_address, timeexceeded) -> rt;
	fr0[1] -> ICMPError($server_address, unreachable, needfrag) -> rt;
	gio0[1] -> ICMPError($server_address, parameterproblem) -> rt;
	cp0[1] -> ICMPError($server_address, redirect, host) -> rt;
	
	// Forwarding path for eth1
	rt[2] -> DropBroadcasts
	-> cp1 :: PaintTee(2)
	-> gio1 :: IPGWOptions($client_address1)
	-> FixIPSrc($client_address1)
	-> dt1 :: DecIPTTL
	-> fr1 :: IPFragmenter(1500)
	-> [0]arpq1;
	dt1[1] -> ICMPError($client_address1, timeexceeded) -> rt;
	fr1[1] -> ICMPError($client_address1, unreachable, needfrag) -> rt;
	gio1[1] -> ICMPError($client_address1, parameterproblem) -> rt;
	cp1[1] -> ICMPError($client_address1, redirect, host) -> rt;

	// Forwarding path for eth2
	rt[3] -> DropBroadcasts
	-> cp2 :: PaintTee(3)
	-> gio2 :: IPGWOptions($client_address2)
	-> FixIPSrc($client_address2)
	-> dt2 :: DecIPTTL
	-> fr2 :: IPFragmenter(1500)
	-> [0]arpq2;
	dt2[1] -> ICMPError($client_address2, timeexceeded) -> rt;
	fr2[1] -> ICMPError($client_address2, unreachable, needfrag) -> rt;
	gio2[1] -> ICMPError($client_address2, parameterproblem) -> rt;
	cp2[1] -> ICMPError($client_address2, redirect, host) -> rt;
/// ===== End - Forwarding Path - Network Layer	
}

///===========================================================================///
/// Definitions of the different hosts and related address information.

// @TODO the idea is that instead of sending the data to a unicast address, it should be sent to 
// a multicast address and that all  clients subscribed to this multicast group receive the UDP 
// packet and print this out.
AddressInfo(multicast_client_address 224.0.0.65)

/// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
/// !!!!!!! DO NOT EDIT BELOW THIS LINE: Any changes made below, will be replaced prior to the project defense !!!!!!!!
/// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 

// Note: The ListenEtherSwitch is used to emulate a local IP network.

server_network :: ListenEtherSwitch;
AddressInfo(router_server_network_address 192.168.1.254/24 00:50:BA:85:84:A1);
AddressInfo(mulicast_server_address 192.168.1.1/24 00:50:BA:85:84:A2);
multicast_server :: IPRouter1int(mulicast_server_address, router_server_network_address);

client_network1 :: ListenEtherSwitch;
AddressInfo(router_client_network1_address 192.168.2.254/24 00:50:BA:85:84:B1);
AddressInfo(client21_address 192.168.2.1/24 00:50:BA:85:84:B2);
AddressInfo(client22_address 192.168.2.2/24 00:50:BA:85:84:B3);
client21 :: IPRouter1int(client21_address, router_client_network1_address);
client22 :: IPRouter1int(client22_address, router_client_network1_address);

client_network2 :: ListenEtherSwitch;
AddressInfo(router_client_network2_address 192.168.3.254/24 00:50:BA:85:84:C1);
AddressInfo(client31_address 192.168.3.1/24 00:50:BA:85:84:C2);
AddressInfo(client32_address 192.168.3.2/24 00:50:BA:85:84:C3);
client31 :: IPRouter1int(client31_address, router_client_network2_address);
client32 :: IPRouter1int(client32_address, router_client_network2_address);

router :: IPRouter3int(router_server_network_address, router_client_network1_address, router_client_network2_address);


///===========================================================================///
/// The configuration of our small IP network

multicast_server[0]
	-> [0]server_network[0]
	-> [0]multicast_server

router[0]
	-> [1]server_network[1]
	-> [0]router
	
client21[0]
	-> [0]client_network1[0]
	-> [0]client21

client22[0]
	-> [1]client_network1[1]
	-> [0]client22

router[1]
	-> [2]client_network1[2]
	-> [1]router

client31[0]
	-> [0]client_network2[0]
	-> [0]client31

client32[0]
	-> [1]client_network2[1]
	-> [0]client32

router[2]
	-> [2]client_network2[2]
	-> [2]router

server_network[2]
	-> ToDump("server_network.dump");

client_network1[3]
	-> ToDump("client_network1.dump");

client_network2[3]
	-> ToDump("client_network2.dump");

/// let the corresponding node send a ping
//ICMPPingSource(mulicast_server_address, client22_address)
RatedSource("data", 1, -1, true)
	-> DynamicUDPIPEncap(mulicast_server_address:ip, 1234, multicast_client_address:ip, 1234) 
	-> EtherEncap(0x0800, mulicast_server_address:eth, mulicast_server_address:eth) /// The MAC addresses here shoudl be from the multicast_server to get past the HostEtherFilter. This way we can reuse the input from the network for the applications.
	-> IPPrint("multicast_server -- UDP ping")
	-> [0]multicast_server

multicast_server[1]
	-> Discard; 

/// packets destined for the mobile node
client21[1]
	-> IPPrint("client21 -- got UDP ping") 
	-> Discard

/// packets destined for the mobile node
client22[1]
	-> IPPrint("client22 -- got UDP ping") 
	-> Discard


/// packets destined for the mobile node
client31[1]
	-> IPPrint("client31 -- got UDP ping") 
	-> Discard

/// packets destined for the mobile node
client32[1]
	-> IPPrint("client32 -- got UDP ping") 
	-> Discard


/// packets destined for the mobile node
router[3]
	-> IPPrint("router -- got UDP ping") 
	-> Discard
